package net.dybai.glacier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtils {
    private static final Logger LOG = LoggerFactory.getLogger(ZipUtils.class);

    // 压缩级别，数值越大文件越小，速度越慢
    private static final int LEVEL = 9;
    // zip 处理文件缓冲区大小
    private static final int BUFSIZE = 1024 * 8; // 8KB

    private static ZipOutputStream beginCompress(File dest) throws IOException {
        OutputStream out = new FileOutputStream(dest);
        return beginCompress(out);
    }

    private static ZipOutputStream beginCompress(OutputStream dest) throws IOException {
        ZipOutputStream zout = new ZipOutputStream(dest);
        zout.setLevel(LEVEL);
        return zout;
    }

    private static void doCompress(ZipOutputStream zout, String path, File src)
            throws IOException {
        byte[] buf = new byte[BUFSIZE];
        int readLen = -1;
        String p = src.getName();
        if (!"/".equals(path)) {
            p = path + "/" + src.getName();
        }
        ZipEntry entry = new ZipEntry(p);

        try (InputStream in = new FileInputStream(src)) {
            zout.putNextEntry(entry);
            while ((readLen = in.read(buf)) >= 0){
                zout.write(buf, 0, readLen);
            }
        }
    }

    private static void endCompress(ZipOutputStream zout) {
        try {
            zout.finish();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            zout.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            zout.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void compress(ZipOutputStream zout, File src, String path
            , Set<String> excludeAbsFiles, Set<String> excludeWildcardFiles)
            throws IOException {
        String absPath = src.getAbsolutePath();
        if (!src.exists()) {
            throw new FileNotFoundException("Src is not found! " + absPath);
        }
        if (Thread.currentThread().isInterrupted()) {
            LOG.error("Thread is interrupted, to be interrupt the recursion compress.");
            return ;
        }
        if ((excludeAbsFiles.size() > 0) && excludeAbsFiles.contains(absPath)) {
            LOG.debug("Exclude " + absPath);
            excludeAbsFiles.remove(absPath);
            return;
        }
        if (excludeWildcardFiles.size() > 0) {
            if (excludeWildcardFiles.contains(src.getName())) {
                LOG.debug("Exclude " + absPath);
                return;
            }
        }
        LOG.debug(absPath);
        if (src.isDirectory()) {
            String p = src.getName();
            if (!"/".equals(path)) {
                p = path + "/" + src.getName();
            }
            File[] files = src.listFiles();
            for (File file : files) {
                compress(zout, file, p, excludeAbsFiles, excludeWildcardFiles);
            }
        } else {
            try {
                doCompress(zout, path, src);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void zip(File src, PipedOutputStream dest
            , Set<String> excludeAbsFiles, Set<String> excludeWildcardFiles)
            throws IOException {
        ZipOutputStream zout = beginCompress(dest);
        compress(zout, src, "/", excludeAbsFiles, excludeWildcardFiles);
        endCompress(zout);
    }

    public static void zip(File src, File dest
            , Set<String> excludeAbsFiles, Set<String> excludeWildcardFiles)
            throws IOException {
        ZipOutputStream zout = beginCompress(dest);
        compress(zout, src, "/", excludeAbsFiles, excludeWildcardFiles);
        endCompress(zout);
    }

    public static void zip(String src, String dest
            , Set<String> excludeAbsFiles, Set<String> excludeWildcardFiles)
            throws IOException {
        zip(new File(src), new File(dest), excludeAbsFiles, excludeWildcardFiles);
    }
}
